package cn.w.outdoor.lib.network;

import android.util.Log;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import cn.w.outdoor.lib.media.OnDataListener;
import cn.w.outdoor.lib.model.Trans_Package;

public class UDPTools {

    private final static String TAG = "UDPTools";

    private static final UDPTools ourInstance = new UDPTools();

    public static UDPTools getInstance() {
        return ourInstance;
    }

    private UDPTools() {
    }

    private final AtomicBoolean control = new AtomicBoolean(false);

    private final static int PORT = 0x6877;
    private final static int RECEIVER_BUFF = 65555;

    private OnDataListener audioDataListener = null;
    private OnDataListener videoDataListener = null;

    private UDPReceiverThread udpReceiverThread = null;
    private UDPSenderThread udpSenderThread = null;

    public void startUDPTools(String localIpAddr) {
        synchronized (control) {
            if (!control.get()) {
                if (udpReceiverThread == null) {
                    udpReceiverThread = new UDPReceiverThread(localIpAddr, PORT);
                    udpReceiverThread.start();
                    udpSenderThread = new UDPSenderThread(localIpAddr, PORT + 1);
                    udpSenderThread.start();
                    control.set(true);
                }
            } else {
                System.err.println("网络部分已经初始化，无法再次执行此方法");
            }
        }
    }

    public void stopUDPTools() {
        synchronized (control) {
            if (control.get()) {
                if (udpReceiverThread != null) {
                    udpReceiverThread.stopReceiver();
                    udpSenderThread.stopThreadController();
                    try {
                        udpReceiverThread.join();
                        udpSenderThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    control.set(false);
                }
            } else {
                System.err.println("网络部分尚未初始化，无法执行此方法");
            }
        }
    }

    public void setOnAudioDataListener(OnDataListener dataListener) {
        this.audioDataListener = dataListener;
    }

    public void setOnVideoDataListener(OnDataListener dataListener) {
        this.videoDataListener = dataListener;
    }

    public void sendMedia(Trans_Package trans_package, String remoteIpAddr, int remotePort) {
        if (udpSenderThread != null) {
            udpSenderThread.sendTransPackage(trans_package, remoteIpAddr, remotePort);
        }
    }

    public void sendMediaAsync(Trans_Package trans_package, String remoteIpAddr, int remotePort) {
        if (udpSenderThread != null) {
            udpSenderThread.sendTransPackageAsync(trans_package, remoteIpAddr, remotePort);
        }
    }

    private class UDPSenderThread extends Thread {

        public class SendUnit {
            public Trans_Package trans_package;
            public String remoteIpAddr;
            public int remotePort;

            public SendUnit() {
            }

            public SendUnit(Trans_Package trans_package, String remoteIpAddr, int remotePort) {
                this.trans_package = trans_package;
                this.remoteIpAddr = remoteIpAddr;
                this.remotePort = remotePort;
            }
        }

        private final Queue<SendUnit> sendUnitQueue = new ConcurrentLinkedQueue<>();

        private DatagramSocket datagramSocket = null;

        private boolean threadController = false;

        private final AtomicBoolean controller = new AtomicBoolean(false);

        public void stopThreadController() {
            threadController = true;
        }

        public UDPSenderThread(String localIpAddr, int port) {
            try {
                datagramSocket = new DatagramSocket(port, InetAddress.getByName(localIpAddr));
            } catch (SocketException | UnknownHostException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            threadController = false;

            int count = 0;

            while (!threadController) {
                SendUnit sendUnit = sendUnitQueue.poll();
                if (sendUnit != null) {
                    sendTransPackage(sendUnit);
                } else {
                    if (++count % 10 == 0) {
                        count = 0;
//                        Log.e(TAG, "run: 等待下一次发送");
                        try {
                            TimeUnit.MILLISECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            datagramSocket.close();
        }

        public void sendTransPackage(SendUnit sendUnit) {
            sendTransPackage(sendUnit.trans_package, sendUnit.remoteIpAddr, sendUnit.remotePort);
        }

        public void sendTransPackage(Trans_Package trans_package, String remoteIpAddr, int remotePort) {
            if (datagramSocket == null) return;
            try {
                synchronized (controller) {
                    datagramSocket.send(new DatagramPacket(trans_package.generateToBytes(), trans_package.generateToBytes().length,
                            InetAddress.getByName(remoteIpAddr), remotePort));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void sendTransPackageAsync(Trans_Package trans_package, String remoteIpAddr, int remotePort) {
            sendUnitQueue.offer(new SendUnit(trans_package, remoteIpAddr, remotePort));
        }

    }

    private class UDPReceiverThread extends Thread {

        private DatagramSocket datagramSocket = null;

        private boolean threadController = false;

        public UDPReceiverThread(String localIpAddr, int port) {
            try {
                datagramSocket = new DatagramSocket(port, InetAddress.getByName(localIpAddr));
            } catch (SocketException | UnknownHostException e) {
                e.printStackTrace();
            }
        }

        public void stopReceiver() {
            threadController = true;
        }

        @Override
        public void run() {
            byte[] receiveBuff = new byte[RECEIVER_BUFF];
            DatagramPacket datagramPacket = new DatagramPacket(receiveBuff, receiveBuff.length);

            try {
                while (!threadController) {
                    if (datagramSocket == null) {
                        Thread.sleep(10L);
                        continue;
                    }
                    datagramSocket.receive(datagramPacket);
                    if (datagramPacket.getLength() > 0) {
                        Trans_Package trans_package = new Trans_Package(receiveBuff);
                        if (trans_package.getvType() == Trans_Package.vType_Audio) {
                            byte[] audioBuffer = new byte[datagramPacket.getLength()];
                            System.arraycopy(receiveBuff, 0, audioBuffer, 0, datagramPacket.getLength());
                            trans_package = new Trans_Package(audioBuffer);
//                            System.out.println("UDP收到 音频 数据内容长度：" + datagramPacket.getLength());
                            if (UDPTools.this.audioDataListener != null) {
                                UDPTools.this.audioDataListener.onData(trans_package);
                            }
                        } else if (trans_package.getvType() == Trans_Package.vType_Video) {
                            byte[] audioBuffer = new byte[datagramPacket.getLength()];
                            System.arraycopy(receiveBuff, 0, audioBuffer, 0, datagramPacket.getLength());
                            trans_package = new Trans_Package(audioBuffer);
//                            System.out.println("UDP收到 视频 数据内容长度：" + datagramPacket.getLength());
                            if (UDPTools.this.videoDataListener != null) {
                                UDPTools.this.videoDataListener.onData(trans_package);
                            } else {
                                System.out.println("");
                            }
                        } else {
                            System.err.println("不符合数据格式，丢弃接收到的数据内容");
                        }
                    } else {
                        System.err.println("数据长度不符合，丢弃接收到的数据内容");
                        Thread.sleep(10L);
                    }
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

}
